// 当文件增多 极有可能出现头文件重复包含 使用预处理指令
// 使得当该头文件已被包含时 不再重复包含该文件
#ifndef __MY_FILEUTIL__
#define __MY_FILEUTIL__

#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <experimental/filesystem>
#include <sys/stat.h>
#include <QDebug>
#include <QFileInfo>
#include <QDateTime>
#include <QFileDevice>
#include <QDir>
#include <iomanip>


// 同一个工程中允许存在多个相同名称的命名空间 最后合成同一个命名空间
namespace cloudBackup
{
// 简洁命名
namespace fs = std::experimental::filesystem;

class FileUtil
{
private:
    std::string _filePath; // ./test.txt /home/lhr/linux/cloud_backup/a/b/c/test.txt

public:
    FileUtil(const std::string& filePath)
        : _filePath(filePath)
    {

    }

    // 获取文件大小
    size_t FileSize()
    {
        struct stat statBuf;
        // int stat(const char *file, stat *buf)
        if (stat(_filePath.c_str(), &statBuf) < 0)
        {
            return 0;
        }
        return statBuf.st_size;
    }

    // 由文件路径获取纯文件名称
    std::string FileName()
    {
        // 将 std::string 转换为 QString
        QString qFilePath = QString::fromStdString(_filePath);

        // 使用 QFileInfo 获取文件信息
        QFileInfo fileInfo(qFilePath);

        // 获取文件名并转换回 std::string
        std::string filename = fileInfo.fileName().toStdString();
        return filename;
    }


    // 获取最后一次访问时间
    time_t LastATime()
    {
        QFileInfo fileInfo(QString::fromStdString(_filePath));
        QDateTime atime = fileInfo.lastRead();
        return atime.toTime_t();
    }

    // 获取最后一次状态改变时间
    time_t LastCTime()
    {
        QFileInfo fileInfo(QString::fromStdString(_filePath));
        if (!fileInfo.exists())
        {
            qWarning() << "File does not exist:" << QString::fromStdString(_filePath);
            return -1; // 或者其他错误处理
        }
        QDateTime stateChangeTime = fileInfo.fileTime(QFileDevice::FileTime::FileMetadataChangeTime);
        //qDebug() << QString::fromStdString(_filePath) << "最后一次状态改变时间" << stateChangeTime;
        return stateChangeTime.toTime_t();
    }

    // 获取最后一次修改时间
    time_t LastMTime()
    {
        QFileInfo fileInfo(QString::fromStdString(_filePath));
        QDateTime mtime = fileInfo.lastModified();
        return mtime.toTime_t();
    }

    // 获取文件pos后len长度的数据 传递给content
    bool GetPosLen(std::string* content, size_t pos, size_t len)
    {
        // pos后文件长度不足len
        size_t fsize = this->FileSize();
        if (pos + len > fsize)
        {
            return false;
        }

        std::ifstream ifs;
        ifs.open(_filePath, std::ios::binary);
        if (ifs.is_open() == false)
        {
            return false;
        }

        ifs.seekg(pos, std::ios::beg);
        content->resize(len);
        // &(*content)[0] == &( (*content)[0] ) 这么做是因为read接收char*类型
        ifs.read(&(*content)[0], len);
        // 检查在读取文件内容的过程中是否发生了错误
        if (ifs.good() == false)
        {
            ifs.close();
            return false;
        }
        ifs.close();
        return true;
    }

    // 获取整个文件内容传递给content #代码复用
    bool GetContent(std::string* content)
    {
        // 读取数据前确保文件有数据
        size_t fsize = this->FileSize();
        if (fsize == 0)
            return false;
        return GetPosLen(content, 0, fsize);
    }

    // 将content中的数据写入到_filePath指向的文件(不存在open会尝试创建)
    bool SetContent(const std::string& content)
    {
        std::ofstream ofs;
        ofs.open(_filePath, std::ios::binary);
        if (ofs.is_open() == false)
        {
            return false;
        }
        ofs.write(&content[0], content.size());
        if (ofs.good() == false)
        {
            ofs.close();
            return false;
        }
        ofs.close();
        return true;
    }

    // 判断文件是否存在
    bool Exists()
    {
        QFileInfo fileInfo(QString::fromStdString(_filePath));
        return fileInfo.exists();
    }

    // 将备份路径下的所有文件名 存至fileArray
    bool ScanDir(std::vector<std::string>& fileArray)
    {
        // 检查传入路径是否存在
        QString QfilePath = QString::fromStdString(_filePath);
        QDir dir(QfilePath);
        if (!dir.exists())
        {
            qDebug() << "Directory does not exist:" << QfilePath;
            return false;
        }

        // 浏览目录下的所有文件
        QStringList files = dir.entryList(QDir::Files); // 获取文件列表
        for (const QString& fileName : files)
        {
            // 将D:/AtestBackup/test.txt文件添加到 fileArray 中
            std::string backup_file = _filePath + "/" + fileName.toStdString();
            fileArray.push_back(backup_file);
        }

        return true;
    }

    static std::string UrlEncode(const std::string &value)
    {
        std::ostringstream escaped; // 创建一个输出字符串流，用于存储编码后的结果
        escaped.fill('0'); // 填充字符为 '0'
        escaped << std::hex; // 设置输出为十六进制格式

        // 遍历输入字符串中的每个字符
        for (char c : value)
        {
            // 检查字符是否是字母数字字符或特定保留字符
            if (isalnum(static_cast<unsigned char>(c)) || c == '-' || c == '_' || c == '.' || c == '~')
            {
                // 如果是保留的字符，直接添加到输出流中
                escaped << c;
            }
            else
            {
                // 对其他字符进行编码
                // 使用 '%' 后面跟上该字符的十六进制 ASCII 码
                escaped << '%' << std::setw(2) << int(static_cast<unsigned char>(c));
            }
        }

        return escaped.str(); // 返回编码后的字符串
    }

};
}

#endif //__MY_FILEUTIL__
